#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Server
{

    using namespace std;

    // 定义默认的Ip 以点分十进制的方式初始化
    static const string defaultIp = "0.0.0.0";

    // enum 枚举退出信息
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR
    };

    class udpServer
    {
    public:
        // 构造函数 -- 构造出udpSever对象
        // 需要传入端口号和IP
        udpServer(const uint16_t &port , const string &ip = defaultIp)
            : _port(port), _ip(ip), _sockfd(-1)
        {
        }

        // 初始化
        void initServer()
        {
            // 1. 创建socket -- 网络套接字
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd == -1)
            {
                cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
                // 采用枚举
                exit(SOCKET_ERR);
            }
            cout << "socket success : " << _sockfd << endl;
            // 2. 绑定port和ip
            // 未来服务器需要明确port 不能随意改动
            struct sockaddr_in local; // 定义变量 在栈上 属于用户态
            //        bzero - write zero-valued bytes -- 全部设置为0
            bzero(&local,sizeof(local));
            // 将sin和family进行拼接
            local.sin_family = AF_INET;
            // 要进行通信需要将自己的port和ip传给对方
            // htons 将自身数据转化为网络字节顺序(大端) 
            // 为啥用htons 因为_port是两个字节
            local.sin_port = htons(_port);
            // ip 因为是点分十进制的 要先转成uin32_t 整型
            // 同样的需要转化为网络字节顺序(大端)
            // 而inet_addr就包含两种需求
            local.sin_addr.s_addr = inet_addr(_ip.c_str());

            // 只有在bind才是真正的绑定进网络 
            // 之前都是在设置，将输入信息设置正确才能连入网络
            // 
            int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
            if(n==-1)
            {
                cerr << "bind error: " << errno << strerror(errno) << endl;
                exit(BIND_ERR); 
            }

            // 走到这里UDP Server的预备工作才完成
        }

        ~udpServer()
        {}
    private:
        // port 端口号
        uint16_t _port;
        // ip地址
        string _ip;
        //
        int _sockfd;
    };
}